package com.base.dp;

/**
 * 一条包含字母 A-Z 的消息通过以下映射进行了 编码 ：
 * <p>
 * "1" -> 'A'
 * <p>
 * "2" -> 'B'
 * <p>
 * ...
 * <p>
 * "25" -> 'Y'
 * <p>
 * "26" -> 'Z'
 * <p>
 * 然而，在 解码 已编码的消息时，你意识到有许多不同的方式来解码，因为有些编码被包含在其它编码当中（"2" 和 "5" 与 "25"）。
 * <p>
 * 例如，"11106" 可以映射为：
 * <p>
 * "AAJF" ，将消息分组为 (1, 1, 10, 6)
 * "KJF" ，将消息分组为 (11, 10, 6)
 * 消息不能分组为  (1, 11, 06) ，因为 "06" 不是一个合法编码（只有 "6" 是合法的）。
 * 注意，可能存在无法解码的字符串。
 * <p>
 * 给你一个只含数字的 非空 字符串 s ，请计算并返回 解码 方法的 总数 。如果没有合法的方式解码整个字符串，返回 0。
 * <p>
 * 题目数据保证答案肯定是一个 32 位 的整数。
 * <p>
 * <p>
 * <p>
 * 示例 1：
 * <p>
 * 输入：s = "12"
 * 输出：2
 * 解释：它可以解码为 "AB"（1 2）或者 "L"（12）。
 * 示例 2：
 * <p>
 * 输入：s = "226"
 * 输出：3
 * 解释：它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
 * 示例 3：
 * <p>
 * 输入：s = "06"
 * 输出：0
 * 解释："06" 无法映射到 "F" ，因为存在前导零（"6" 和 "06" 并不等价）。
 * <p>
 * <p>
 * 提示：
 * <p>
 * 1 <= s.length <= 100
 * s 只包含数字，并且可能包含前导零。
 */
public class NumDecodings {

    public static void main(String[] args) {
        NumDecodings numDecodings = new NumDecodings();
        System.out.println(numDecodings.numDecodings("12"));
    }

    public int numDecodings(String s) {
        //初始化：创建一个 DP 数组 dp，长度为 s.length() + 1，并将 dp[0] 初始化为 1，因为空字符串有一种解码方法（即不解码）。其余的 dp[i] 初始化为 0。
        //状态转移：对于字符串 s 中的每个位置 i（从 1 到 s.length()），我们需要检查以下两种情况：
        //如果 s[i-1] 是一个有效的编码（即 s[i-1] != '0'），那么 dp[i] 至少应该加上 dp[i-1]。
        //如果 s[i-1] 和 s[i-2] 组成的数字在 10 到 26 之间，那么 dp[i] 还应该加上 dp[i-2]。
        //边界条件：在检查 s[i-2] 和 s[i-1] 组成的数字时，需要确保 i-2 是一个有效的索引
        if (s == null || s.isEmpty()) return 0;
        int[] dp = new int[s.length() + 1];
        // dp[i] 表示字符串 s 的前 i 个字符的解码方法总数。
        dp[0] = 1; // 空字符串有一种解码方法
        dp[1] = s.charAt(0) != '0' ? 1
                : 0; // 第一个字符不是 '0' 才有一种解码方法

        for (int i = 2; i <= s.length(); i++) {
            int oneDigit = Integer.parseInt(s.substring(i - 1, i)); // 获取 s[i-1]
            int twoDigits = Integer.parseInt(s.substring(i - 2, i)); // 获取 s[i-2] 和 s[i-1]

            // 如果 s[i-1] 不是 '0'，则至少可以单独解码
            if (oneDigit >= 1) {
                dp[i] += dp[i - 1];
            }
            // 如果 s[i-2] 和 s[i-1] 组成的数字在 10 到 26 之间，则可以组合解码
            if (twoDigits >= 10 && twoDigits <= 26) {
                dp[i] += dp[i - 2];
            }
        }
        return dp[s.length()];
    }
}
